import { PrismaClient } from "@prisma/client";
import Redis, { RedisOptions } from 'ioredis';
import logger from "../utils/logger";

// 数据库连接状态枚举
enum DatabaseStatus {
    DISCONNECTED = 'disconnected',
    CONNECTING = 'connecting',
    CONNECTED = 'connected',
    ERROR = 'error'
}

// Redis连接配置接口
interface RedisConfig extends RedisOptions {
    // 重试延迟
    retryDelayOnFailover?: number;
    // 最大重连次数
    maxRetriesPerRequest?: number;
    // 启用自动管道
    enableAutoPipelining?: boolean;
    // 延迟连接
    lazyConnect?: boolean;
    // 重试策略
    retryPolicy?: (times: number) => number;
}

// 数据库连接管理类
class DatabaseManager {
    private static instance: DatabaseManager;
    private prismaClient: PrismaClient | null = null;
    private redisClient: Redis | null = null;
    private mysqlStatus: DatabaseStatus = DatabaseStatus.DISCONNECTED;
    private redisStatus: DatabaseStatus = DatabaseStatus.DISCONNECTED;

    private constructor() {
        // 私有构造函数，确保单例模式
    }

    // 获取数据库管理器实例（单例模式）
    public static getInstance(): DatabaseManager {
        if (!DatabaseManager.instance) {
            DatabaseManager.instance = new DatabaseManager();
        }
        return DatabaseManager.instance;
    }

    // 初始化 Prisma MySQL 连接
    private async initializePrisma(): Promise<PrismaClient> {
        if (this.prismaClient && this.mysqlStatus === DatabaseStatus.CONNECTED) {
            return this.prismaClient;
        }

        try {
            this.mysqlStatus = DatabaseStatus.CONNECTING;
            logger.info('正在初始化 MySQL 数据库连接...');

            // Prisma 客户端配置
            const prismaOptions = {
                log: [
                    {
                        emit: 'event' as const,
                        level: 'query' as const,
                    },
                    {
                        emit: 'event' as const,
                        level: 'error' as const,
                    },
                    {
                        emit: 'event' as const,
                        level: 'info' as const,
                    },
                    {
                        emit: 'event' as const,
                        level: 'warn' as const,
                    },
                ],
                // 错误格式化
                errorFormat: 'pretty' as const,
            };

            this.prismaClient = new PrismaClient(prismaOptions);

            // 设置查询日志（仅开发环境）
            if (process.env.NODE_ENV === 'development') {
                (this.prismaClient as any).$on('query', (e: any) => {
                    logger.debug(`查询: ${e.query} | 参数: ${e.params} | 耗时: ${e.duration}ms`);
                });
            }

            // 设置错误日志
            (this.prismaClient as any).$on('error', (e: any) => {
                logger.error('Prisma 错误:', e);
            });

            (this.prismaClient as any).$on('info', (e: any) => {
                logger.info('Prisma 信息:', e.message);
            });

            (this.prismaClient as any).$on('warn', (e: any) => {
                logger.warn('Prisma 警告:', e.message);
            });

            // 测试连接
            await this.prismaClient.$connect();
            await this.prismaClient.$queryRaw`SELECT 1`;

            this.mysqlStatus = DatabaseStatus.CONNECTED;
            logger.info('✅ MySQL 数据库连接成功');

            return this.prismaClient;
        } catch (error) {
            this.mysqlStatus = DatabaseStatus.ERROR;
            logger.error('❌ MySQL 数据库连接失败:', error);
            throw new Error(`MySQL 连接失败: ${error instanceof Error ? error.message : '未知错误'}`);
        }
    }

    // 初始化 Redis 连接
    private async initializeRedis(): Promise<Redis> {
        if (this.redisClient && this.redisStatus === DatabaseStatus.CONNECTED) {
            return this.redisClient;
        }

        try {
            this.redisStatus = DatabaseStatus.CONNECTING;
            logger.info('正在初始化 Redis 连接...');

            // Redis 连接配置
            const redisConfig: RedisConfig = {
                host: process.env.REDIS_HOST || 'localhost',
                port: parseInt(process.env.REDIS_PORT || '6379'),
                password: process.env.REDIS_PASSWORD || undefined,
                db: parseInt(process.env.REDIS_DB || '0'),

                // 性能优化配置
                enableAutoPipelining: true,  // 启用自动管道
                lazyConnect: true,           // 延迟连接
                maxRetriesPerRequest: 3,     // 每个请求最大重试次数
                retryDelayOnFailover: 100,   // 故障转移重试延迟

                // 连接池配置
                family: 4,                   // IPv4
                keepAlive: 30000,            // 保持连接活跃(毫秒)
                connectTimeout: 10000,       // 连接超时 10s
                commandTimeout: 5000,        // 命令执行超时 5s

                // 重连配置
                retryPolicy: (times: number) => {
                    const delay = Math.min(times * 50, 2000);
                    return delay;
                },
            };

            this.redisClient = new Redis(redisConfig);

            // Redis 事件监听
            this.redisClient.on('connect', () => {
                this.redisStatus = DatabaseStatus.CONNECTED;
                logger.info('✅ Redis 连接成功');
            });

            this.redisClient.on('ready', () => {
                logger.info('✅ Redis 准备就绪');
            });

            this.redisClient.on('error', (error: any) => {
                this.redisStatus = DatabaseStatus.ERROR;
                logger.error('❌ Redis 连接错误:', error);
            });

            this.redisClient.on('close', () => {
                this.redisStatus = DatabaseStatus.DISCONNECTED;
                logger.warn('⚠️ Redis 连接已关闭');
            });

            this.redisClient.on('reconnecting', (time: any) => {
                this.redisStatus = DatabaseStatus.CONNECTING;
                logger.info(`🔄 Redis 重连中... (${time}ms后重试)`);
            });

            // 手动连接（因为启用了 lazyConnect）
            await this.redisClient.connect();

            // 测试连接
            await this.redisClient.ping();

            return this.redisClient;
        } catch (error) {
            this.redisStatus = DatabaseStatus.ERROR;
            logger.error('❌ Redis 连接失败:', error);
            throw new Error(`Redis 连接失败: ${error instanceof Error ? error.message : '未知错误'}`);
        }
    }

    // 获取 Prisma 客户端
    public async getPrisma(): Promise<PrismaClient> {
        if (!this.prismaClient || this.mysqlStatus !== DatabaseStatus.CONNECTED) {
            return await this.initializePrisma();
        }
        return this.prismaClient;
    }

    // 获取 Redis 客户端
    public async getRedis(): Promise<Redis> {
        if (!this.redisClient || this.redisStatus !== DatabaseStatus.CONNECTED) {
            return await this.initializeRedis();
        }
        return this.redisClient;
    }

    // 初始化所有数据库连接
    public async initialize(): Promise<{ prisma: PrismaClient; redis: Redis }> {
        try {
            logger.info('🚀 开始初始化数据库连接...');

            // 并行初始化两个数据库连接以提高性能
            const [prisma, redis] = await Promise.all([
                this.initializePrisma(),
                this.initializeRedis()
            ]);

            logger.info('✅ 所有数据库连接初始化完成');

            return { prisma, redis };
        } catch (error) {
            logger.error('❌ 数据库初始化失败:', error);
            throw error;
        }
    }

    // 获取连接状态
    public getStatus() {
        return {
            mysql: {
                status: this.mysqlStatus,
                connected: this.mysqlStatus === DatabaseStatus.CONNECTED,
            },
            redis: {
                status: this.redisStatus,
                connected: this.redisStatus === DatabaseStatus.CONNECTED,
            },
        };
    }

    // 健康检查
    public async healthCheck(): Promise<{ mysql: boolean; redis: boolean }> {
        const results = {
            mysql: false,
            redis: false,
        };

        try {
            // MySQL 健康检查
            if (this.prismaClient) {
                await this.prismaClient.$queryRaw`SELECT 1`;
                results.mysql = true;
            }
        } catch (error: any) {
            logger.error('MySQL 健康检查失败:', error);
        }

        try {
            // Redis 健康检查
            if (this.redisClient) {
                const result = await this.redisClient.ping();
                results.redis = result === 'PONG';
            }
        } catch (error: any) {
            logger.error('Redis 健康检查失败:', error);
        }

        return results;
    }

    // 关闭连接
    public async disconnect(): Promise<void> {
        logger.info('🔄 开始关闭数据库连接...');

        const promises: Promise<void>[] = [];

        // 关闭 Prisma 连接
        if (this.prismaClient && this.mysqlStatus === DatabaseStatus.CONNECTED) {
            promises.push(
                this.prismaClient.$disconnect().then(() => {
                    this.mysqlStatus = DatabaseStatus.DISCONNECTED;
                    logger.info('✅ MySQL 连接已关闭');
                }).catch((error: any) => {
                    logger.error('❌ 关闭 MySQL 连接时出错:', error);
                })
            );
        }

        // 关闭 Redis 连接
        if (this.redisClient && this.redisStatus === DatabaseStatus.CONNECTED) {
            promises.push(
                new Promise<void>((resolve, reject) => {
                    this.redisClient!.disconnect(false);
                    this.redisStatus = DatabaseStatus.DISCONNECTED;
                    logger.info('✅ Redis 连接已关闭');
                    resolve();
                }).catch((error: any) => {
                    logger.error('❌ 关闭 Redis 连接时出错:', error);
                })
            );
        }

        // 等待所有连接关闭
        await Promise.allSettled(promises);
        logger.info('✅ 所有数据库连接已关闭');
    }

    // Redis 缓存操作封装
    public async cacheGet(key: string): Promise<string | null> {
        try {
            const redis = await this.getRedis();
            return await redis.get(key);
        } catch (error: any) {
            logger.error(`Redis 获取缓存失败 [${key}]:`, error);
            return null;
        }
    }

    public async cacheSet(key: string, value: string, ttl?: number): Promise<boolean> {
        try {
            const redis = await this.getRedis();
            if (ttl) {
                await redis.setex(key, ttl, value);
            } else {
                await redis.set(key, value);
            }
            return true;
        } catch (error: any) {
            logger.error(`Redis 设置缓存失败 [${key}]:`, error);
            return false;
        }
    }

    public async cacheDel(key: string): Promise<boolean> {
        try {
            const redis = await this.getRedis();
            const result = await redis.del(key);
            return result > 0;
        } catch (error: any) {
            logger.error(`Redis 删除缓存失败 [${key}]:`, error);
            return false;
        }
    }

    // 数据库事务支持
    public async transaction<T>(
        fn: (prisma: any) => Promise<T>
    ): Promise<T> {
        const prisma = await this.getPrisma();
        return await prisma.$transaction(fn);
    }
}

// 导出单例实例
const databaseManager = DatabaseManager.getInstance();

// 导出常用方法的快捷方式
export const db = {
    // 获取客户端
    prisma: () => databaseManager.getPrisma(),
    redis: () => databaseManager.getRedis(),

    // 初始化
    initialize: () => databaseManager.initialize(),

    // 状态检查
    status: () => databaseManager.getStatus(),
    healthCheck: () => databaseManager.healthCheck(),

    // 连接管理
    disconnect: () => databaseManager.disconnect(),

    // 缓存操作
    cache: {
        get: (key: string) => databaseManager.cacheGet(key),
        set: (key: string, value: string, ttl?: number) => databaseManager.cacheSet(key, value, ttl),
        del: (key: string) => databaseManager.cacheDel(key),
    },

    // 事务
    transaction: <T>(fn: (prisma: PrismaClient) => Promise<T>) => databaseManager.transaction(fn),
};

export default databaseManager;
